<TeXmacs|1.99.16>

<project|rehash.tm>

<style|<tuple|tmmanual|british>>

<\body>
  <section|Interruptible I/O for fibers>

  guile-fibers doesn't allow for interrupting reads and writes from ports,
  e.g. with a signalled condition variable, or a message from a channel. This
  module <scm|(remirror intr-io)> allows for interrupting reads and writes
  with other operations, including soft ports. Technically, as this module is
  implemented, the I/O is actually is asynchronuous, so <scm|(remirror
  async-io)> might be a better name, except this isn't relied upon in
  remirror anywhere.

  In this module, \<#2018\>port\<#2019\> refers to an object of class
  <scm|\<less\>i/o-port\<gtr\>> and \<#2018\>RnRS port\<#2019\> to ports as
  can be found in the <scm|(rnrs io ports)> module. At time of writing, these
  are disjoint.

  <\warning>
    The guile-fibers warns that soft port and R6RS cusom binary port
    callbacks have continuation barriers, and therefore are not very useful
    to use with guile-fibers, so these mechanisms cannot be used in
    <scm|(remirror intr-io)>. In case anyone hacking on guile is reading
    this: this module can be licensed on tems suitable for inclusion (after
    modifications presumably) in guile itself, just send an e-mail to the
    maintainer or open an issue on the issue tracker. There will be no
    copyright assignments though.
  </warning>

  The structure of <scm|(remirror intr-io)> is partially modelled after
  GNOME's GIO.

  <\warning>
    Unless specified otherwise, input and output refers to the input and
    output of bytes. Unlike in guile, textuals ports and binary ports are
    separated.
  </warning>

  <\warning>
    Not all port types support concurrent access to the same port.
  </warning>

  <\remark>
    All procedures defined here are GOOPS methods, and can be extended as
    such. <todo|investigate how this interacts with module reloading at some
    point in the future. Java-style interfaces seem ideal for most methods.>
  </remark>

  <\convention>
    Predicates must not perform any I/O or block, unless specified otherwise.
  </convention>

  <todo|Split this out in a separate project maybe>

  <subsection|Implementation limitations>

  <\itemize>
    <item>Some methods of the BSD Socket API bindings silently ignore the
    cancellation channel arguments, as the underlying C API's don't support a
    non-blocking mode of implementation. For example, <scm|socket-address>,
    <scm|bind> and <scm|make-socket> can block. This probably won't be much
    of a problem on monolithical kernels, but isn't very ideal for
    microkernels.

    <item>Plenty of missing procedures, which may be added on-demand

    <item>No asynchronuous file I/O (TODO: io uring on Linux, ??? on Hurd,
    maybe aio_???, io_???, thread pool, maybe direct RPC on the Hurd)
  </itemize>

  <subsection|Cancellations>

  Procedures for port operations that can be cancelled can be passed an
  optional <scm-arg|cancel-port> channel. By writing a message to this
  channel, this message will be returned instead by the procedure. It is
  advisable to make sure cancellation messages cannot be confused for normal
  return values (where \<#2018\>normal\<#2019\> may include end-of-file
  objects and the like).

  Be aware that sending a cancellation message will block if no fiber is
  waiting for such a message! For self-documenting code, the following
  aliases of <scm|put-operation> and <scm-arg|get-operation> are defined:

  <\explain>
    <scm|(get-cancellation-operation <scm-arg|cancel-channel>)>
  </explain|An operation that waits for a cancellation from
  <var|cancel-channel> and returns it.>

  <\explain>
    <scm|(put-cancellation-operation <scm-arg|cancel-channel> <scm-arg|msg>)>
  <|explain>
    An operation that tries to send a cancellation message <var|msg> to
    <var|cancel-channel>.
  </explain>

  <\explain>
    <scm|%broken-channel>
  </explain|A channel that must never be written to, and therefore can be
  used as cancellation channel when no cancellation is desired.>

  <subsection|Common port operations>

  <scm|(close-port! <scm-arg|port>)>

  <subsection|Binary stream I/O>

  <\explain>
    <scm|(read-bv! <scm-arg|port> <scm-arg|cancel-channel> <scm-arg|bv>
    <scm-arg|offset> <scm-arg|length> )><explain-synopsis|Read bytes>
  <|explain>
    Try to read some bytes from the port <scm-arg|port>, into the bytevector
    <scm-arg|bv>. On success, return the number of bytes read (\<gtr\> 0) and
    <scm|#f>. Return the end-of-file object and <scm|#f> if at the end of the
    file. Alternatively, raise an appropriate input/output error.

    In case this call is cancelled, it may be the case some or none bytes
    were read. In that case, the number of read bytes and the cancellation
    message are returned.
  </explain>

  <\explain>
    <scm|(write-bv! <scm-arg|port> <scm-arg|cancel-channel> <scm-arg|bv>
    <scm-arg|offset> <scm-arg|length>)><explain-synopsis|Write bytes>
  <|explain>
    Try to write some bytes to the port <scm-arg|port>, into the bytevector
    <scm-arg|bv>. On (partial) success, return the number of bytes written
    and <scm|#f>. Return the end-of-file object and <scm|#f> if at the end of
    the file. Alternatively, raise an appropriate input/output error.

    In case this call is cancelled, it may be the case some bytes were
    written. In that case, the number of written bytes and the cancellation
    message are returned.

    In practice, files can grow indefinitely (up to out of space errors) and
    sockets can be written to indefinitely (up to various network I/O
    errors), so it is unlikely an end-of-file object is returned.
  </explain>

  <\explain>
    <scm|(flush-output-port! <scm-arg|port>
    <scm-arg|cancel-channel>)><explain-synopsis|Flush bytes to write>
  <|explain>
    In case some bytes are stuck in an output port due to some kind of
    buffering, flush the bytes through. When done successfully, return
    <scm|#t>. In case of an I/O error, raise that error. When cancelled,
    return the cancellation message. After a cancellation, perhaps some, but
    not all bytes are flushed, in which case this procedure may be called
    again to try to continue to flush out the remainder. In case of an I/O
    error, it depends on the port and error whether trying to continue to
    flush will lead to any good.
  </explain>

  <\explain>
    <scm|(input-port? <scm-arg|port>)><explain-synopsis|Test if object is an
    input port>

    <scm|(output-port? <scm-arg|port>)><explain-synopsis|Test if object is an
    output port>
  <|explain>
    Test if <var|port> is an input or output port respectively, and return
    <scm|#t> if this is the case. For non-port objects, this will return
    <scm|#f>. The port type may not spontanuously mutate.
  </explain>

  <subsection|Network sockets>

  <\explain>
    <scm|(accept <scm-arg|port> <scm-arg|cancel-channel>)><explain-synopsis|Accept
    a connecton>
  <|explain>
    Accept a connection from <var|port>, and return it as a port. On some
    weird port types, this may return an end-of-file object if there will be
    no further connections. In case of an I/O error, raise an appropriate
    exception and don't accept the connection. When cancelled, don't accept
    any connection, and return the cancellation message.

    <todo|I/O exceptions>
  </explain>

  <scm|(port-address <scm-arg|port> <scm-arg|cancel-channel>)><explain-synopsis|TODO,
  implement and work out specifics>

  <scm|(listen-port? <scm-arg|port>)><explain-synopsis|Test if connections
  may be accepted at some point>

  <\explain>
    <scm|(make-socket <scm-arg|cancel-channel> family style
    proto)><explain-synopsis|Make a network socket>
  <|explain>
    Make a socket, that may be used for some kind of communication specified
    by <var|family>, <var|style> and <var|proto>. If cancelled, return the
    cancellation message instead. This can raise an I/O exception, e.g. if no
    server is implementing <var|family> at the time. The implementation for
    BSD sockets is in <scm|(remirror intr-io bsd-sockets)>, other
    implementation may have different arguments.

    <todo|exceptions>
  </explain>

  <\explain>
    <scm|(socket-bind <scm-arg|port> <scm-arg|cancel-channel>
    <scm-arg|address>)><explain-synopsis|Bind an address to a socket>
  <|explain>
    Try to bind a socket <scm-arg|port> to an address <var|address>,
    returning <scm|#t>. This can fail if <var|port> already is bound to some
    address, though this depends on the port implementation. In case of a I/O
    exception or cancellation, the exception is raised or the cancellation
    message is returned respectively, and it depends on the port
    implementation whether <scm|port> is still usable. It should be fine to
    close <var|port> in such a case, though.

    <todo|exceptions>
  </explain>

  <\explain>
    <scm|(socket-listen <scm-arg|port> <scm-arg|cancel-channel>
    <scm-arg|backlog>)><explain-synopsis|Listen for new connections>
  <|explain>
    Mark <var|port> as a socket that can accept connections, where
    connections may be dropped if more than <var|backlog> (exact natural
    number) are waiting and returing <scm|#t> on success. An I/O error may be
    raised in case of a failure, and in case of a cancellation, the
    cancellation message is returned. If cancelled, the operation can be
    retried later.

    Depending on the port implementation, this can fail if <var|port> is
    already marked as a passive socket.
  </explain>

  <subsection|TLS connections>

  remirror uses GnuTLS for TLS connections. TODO implement

  <subsection| Representing ports as file descriptors>

  <\scm>
    (spawn-with-scheme-port <scm-arg|port> <scm-arg|#:read> <scm-arg|#:write>
    <scm-arg|#:shutdown> #:write-)
  </scm>

  \ ???
</body>

<\initial>
  <\collection>
    <associate|save-aux|false>
  </collection>
</initial>